// NO WARRANTY IS GRANTED. THIS PLUG-IN IS PROVIDED ON AN "AS IS" BASIS, WITHOUT
// WARRANTY OF ANY KIND. NO LIABILITY IS GRANTED, INCLUDING, BUT NOT LIMITED TO,
// ANY DIRECT OR INDIRECT,  SPECIAL,  INCIDENTAL OR CONSEQUENTIAL DAMAGE ARISING
// OUT OF  THE  USE  OR INABILITY  TO  USE  THIS PLUG-IN,  COMPUTER FAILTURE  OF
// MALFUNCTION INCLUDED.  THE USE OF THE SOURCE CODE,  EITHER  PARTIALLY  OR  IN
// TOTAL, IS ONLY GRANTED,  IF USED IN THE SENSE OF THE AUTHOR'S INTENTION,  AND
// USED WITH ACKNOWLEDGEMENT OF THE AUTHOR. FURTHERMORE IS THIS PLUG-IN A  THIRD
// PARTY CONTRIBUTION,  EVEN IF INCLUDED IN REAPER(TM),  COCKOS INCORPORATED  OR
// ITS AFFILIATES HAVE NOTHING TO DO WITH IT.  LAST BUT NOT LEAST, BY USING THIS
// PLUG-IN YOU RELINQUISH YOUR CLAIM TO SUE IT'S AUTHOR, AS WELL AS THE CLAIM TO
// ENTRUST SOMEBODY ELSE WITH DOING SO.
//
//
//----------------------------------------------------------------
// This plugin has taken work from schwa's (reaper.fm, stillwellaudio.com) excellent JSFX FFT-Splitter
// and would not have been possible without IXix' watch library (see: http://forum.cockos.com/showpost.php?p=1386701&postcount=13)
//----------------------------------------------------------------

desc: FFT Vocoder
slider1:0<0,1,1 {carrier: 1 & 2        mod: 3 & 4,carrier: 3 & 4        mod: 1 & 2}> invert input channels
slider2:2<0,4,1{ 128, 256, 512, 1024, 2048}> FFT size
slider4:20<0,100,0.001> clarity [%]
slider5:-50<-120,0,1> white noise mix [dB]
slider6:0<-120,30,1> modulator mix [dB]
slider7:15000<20,20000,1> modulator mix cutoff [Hz]
slider9:2<0,200,1> attack [ms]
slider10:100<0,2000,1> release [ms]
slider11:0<-60,12,1> volume out [dB]

@init 

	fftsize = -1;
	pdc_bot_ch = 0;
	pdc_top_ch = 8;

	maxfft=8192;
	bufOffs=2*maxfft;
	bufwindow=bufOffs*0;
	bufIn1W1 = bufOffs*1;
	bufIn1W2 = bufOffs*2;
	bufIn2W1 = bufOffs*3;
	bufIn2W2 = bufOffs*4;
	bufOutW1 = bufOffs*5;
	bufOutW2 = bufOffs*6;
	envOut = bufOffs*7;
	hiSh = bufOffs*8;
	
	res=1;	

@slider

	sliderfft = (2^(slider2+7))|0;
	fftsize != sliderfft ? (
		memset(bufwindow, 0, fftsize); 
		fftsize = sliderfft;
		w = 2.0*3.14159/fftsize;
		i = 0;
		loop(fftsize/2,
			bufwindow[i] = 0.42-0.50*cos(i*w)+0.08*cos(2.0*i*w);		
			i += 1;
		); 
		
		memset(bufIn1W1, 0, 2*fftsize);
		memset(bufIn1W2, 0, 2*fftsize);
		memset(bufIn2W1, 0, 2*fftsize);
		memset(bufIn2W2, 0, 2*fftsize);
		memset(bufOutW1, 0, 2*fftsize);
		memset(bufOutW2, 0, 2*fftsize);
		memset(envOut, 0, 2*fftsize);
		
		pos = 0;
		pdc_delay = fftsize;
	);
	
	
	memset(hiSh, 1, 2*fftsize);
 	i=0;
	loop(fftsize/2-1,		
		hiSh[i+2]=hiSh[i+3]=(slider4*2/fftsize)*i+1;
		i+=2;
	);

	vol=10^(slider11/20);
	noiseVol=10^(slider5/20);
	modMixVol=10^(slider6/20);
	aCoef=exp(-1/((srate/fftsize)*(slider9/1000)));		
	rCoef=exp(-1/((srate/fftsize)*(slider10/1000)));
	inFlg=slider1;
	

	cc=tan($pi * slider7 / srate);
	a1=1/(1+res*cc+cc^2);
	a2=-2*a1;
	a3=a1;
	b1=2*(cc^2-1)*a1;
	b2=(1-res*cc+cc^2)*a1;
	
 
@block

	intrpolVol = srcVol;						
	dVol = (vol - srcVol)/samplesblock;		
	srcVol = vol;		

@sample
	
	pos >= fftsize ? (
	
		
		fft(bufIn1W1, fftsize);		
		fft_permute(bufIn1W1, fftsize);		
		fft(bufIn2W1, fftsize);		
		fft_permute(bufIn2W1, fftsize);	

		
		i=2;
		loop(fftsize-1,		
			curRe1=bufIn1W1[i];
			curIm1=bufIn1W1[i+1];
			curRe2=bufIn2W1[i];
			curIm2=bufIn2W1[i+1];

			envIn = sqrt(curRe2^2+curIm2^2);

			CurrEnvOut = envOut[i];
			CurrEnvOut < envIn ? (
				envOut[i] = envIn + aCoef*(CurrEnvOut - envIn);
			) : (
				envOut[i] = envIn + rCoef*(CurrEnvOut - envIn);
			);
			mag2 = envOut[i];
			
			bufOutW1[i]=mag2*curRe1*hiSh[i];
			bufOutW1[i+1]=mag2*curIm1*hiSh[i+1];
			
			i+=2;
		);
		
		
		i = 0;
		sumRe = sumIm = 0.0;
		loop(fftsize/2,
			sumRe += bufOutW1[i+2] + bufOutW1[2*fftsize-i-2];			
			sumIm += bufOutW1[i+3] + bufOutW1[2*fftsize-i-1];
			i += 2;
		);
		bufOutW1[0] = -sumRe;
		bufOutW1[1] = -sumIm;
		

		
		fft_ipermute(bufOutW1, fftsize);
		ifft(bufOutW1, fftsize);

		
		
		tmp = bufIn1W1;
		bufIn1W1 = bufIn1W2;
		bufIn1W2 = tmp;
		
		tmp = bufIn2W1;
		bufIn2W1 = bufIn2W2;
		bufIn2W2 = tmp;

		tmp = bufOutW1;
		bufOutW1 = bufOutW2;
		bufOutW2 = tmp;
		
		pos=0;
	);

	inFlg ? (
		modL=spl0;
		modR=spl1;
		carL=spl2;
		carR=spl3;
	):(
		modL=spl2;
		modR=spl3;
		carL=spl0;
		carR=spl1;
	);

	w1 = bufwindow[pos/2];
	w2 = bufwindow[(fftsize-pos)/2-1];
	sw = w1+w2;

	noiseL=(rand(2)-1)*noiseVol;
	noiseR=(rand(2)-1)*noiseVol;
	
	bufIn1W1[fftsize+pos] = w2*(carL+noiseL);
	bufIn1W1[fftsize+pos+1] = w2*(carR+noiseR);
	bufIn1W2[pos] = w1*(carL+noiseL);	
	bufIn1W2[pos+1] = w1*(carR+noiseR);
	
	bufIn2W1[fftsize+pos] = w2*modL;		
	bufIn2W1[fftsize+pos+1] = w2*modR;
	bufIn2W2[pos] = w1*modL;		
	bufIn2W2[pos+1] = w1*modR;

	intrpolVol+=dVol;
	
	modLFil = a1*modL + a2*lastModL + a3*last2ModL - b1*lastModLFil - b2*last2ModLFil;
	last2ModL=lastModL;
	lastModL=modL;
	last2ModLFil=lastModLFil;
	lastModLFil=modLFil;
	
	modRFil = a1*modR + a2*lastModR + a3*last2ModR - b1*lastModRFil - b2*last2ModRFil;
	last2ModR=lastModR;
	lastModR=modR;
	last2ModRFil=lastModRFil;
	lastModRFil=modRFil;
	
	
	spl0 = ((bufOutW2[pos]+bufOutW1[fftsize+pos])/(16*fftsize*sw) + modLFil*modMixVol) * intrpolVol;			
	spl1 = ((bufOutW2[pos+1]+bufOutW1[fftsize+pos+1])/(16*fftsize*sw) + modRFil*modMixVol) * intrpolVol;

	pos += 2;		

  
